package cc.wanforme.chipmunity.config;


import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import cc.wanforme.chipmunity.security.handlers.CustomAuthenticationFailureHandler;
import cc.wanforme.chipmunity.security.handlers.CustomAuthenticationSuccessHandler;
import cc.wanforme.chipmunity.security.handlers.CustomJSONLoginFilter;
import cc.wanforme.chipmunity.security.handlers.CustomLogoutSuccessHandler;
import cc.wanforme.chipmunity.security.handlers.CustomTokenFilter;
import cc.wanforme.chipmunity.security.service.PersistentLoginService;
import cc.wanforme.chipmunity.security.service.UserDetailsServiceImpl;
import cc.wanforme.chipmunity.system.service.interfaces.LoginLogService;

/**Spring 安全配置
 * @author wanne
 * 2019年7月22日
 * 
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
	/*
	 * @EnableGlobalMethodSecurity(prePostEnabled=true) 最后一个注解启用方法级的安全控制，
	 * 控制层方法前加上 @PreAuthorize("hasAuthority('permission')")要求具有permission权限、或@PreAuthorize("hasRole('USER')")要求为USER角色
	 * 
	 */
	
	/*@Autowired
	@Qualifier("defaultDb")
	private DataSource dataSource;
	*/
	
	@Autowired
	private DataSource dataSource;

	private UserDetailsService userDetailsService;
	
	private LoginLogService loginLogService;
	
	private PersistentLoginService persistentLoginService;
	
    @Autowired
    public void setUserDetailsService(UserDetailsServiceImpl userDetailsService){
        this.userDetailsService = userDetailsService;
    }
    
    @Autowired
    public void setLoginLogService(LoginLogService loginLogService) {
		this.loginLogService = loginLogService;
	}
    
    @Autowired
    public void setPersistentLoginService(PersistentLoginService persistentLoginService) {
		this.persistentLoginService = persistentLoginService;
	}
    
    @Bean
    public JdbcTokenRepositoryImpl persistentTokenRepository() {
    	JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
    	jdbcTokenRepositoryImpl.setDataSource(dataSource);
    	return jdbcTokenRepositoryImpl;
    }
    
	/** 自定义路径的权限*/
	@Override
	public void configure(HttpSecurity http) throws Exception {
		//http.csrf();
		
		http
			.authorizeRequests()
			.antMatchers("/", "/index", "/login", "/register").permitAll()
			.antMatchers("/index.html", "/login.html", "/register.html").permitAll()
			.antMatchers("/per.html").hasAuthority("passage:reply")
/*			.antMatchers("/user/**").hasAuthority("USER")
			.antMatchers("/admin/**").hasAuthority("ADMIN")
			.antMatchers("/per.html").hasAuthority("USER")*/
			/*.and() 这是表单的登录方式，使用thymeleaf
			.formLogin().loginPage("/login").defaultSuccessUrl("/per")
			.and()
			.logout().logoutUrl("/logout").logoutSuccessUrl("/login");*/
			.and()
				.logout().logoutUrl("/logout")
				.logoutSuccessHandler(new CustomLogoutSuccessHandler(persistentLoginService))
				//.invalidateHttpSession(true) //注销之后，让sesson失效，或者用下面的清楚授权
				.clearAuthentication(true).permitAll();
			/* 记住我（不适用前后端 分离类型）
				.and()
				.rememberMe()
				.alwaysRemember(false) // 没有传递对应参数时，不记住
				.tokenRepository(persistentTokenRepository()) //设置repository
				.tokenValiditySeconds(60*60*24*30) //有效期30天
				.userDetailsService(userDetailsService); //设置userDetailsService，还好之前自定义登录过滤器的时候没有重新写一个Service，不然就出现两个功能一样的service了
			*/
				
		// 加http.csrf().disable()是为了防止报这个错Whitelabel Error Page
		http.csrf().disable(); //允许跨域伪造请求

		// 添加token验证
		http.addFilterBefore(new CustomTokenFilter(userDetailsService, loginLogService, persistentLoginService),
				UsernamePasswordAuthenticationFilter.class);
		// 添加过滤器，登录 认证
		http.addFilterAt(customJSONLoginFilter(), UsernamePasswordAuthenticationFilter.class);
	}
	
    /**
     * 自定义认证过滤器
     */
    private CustomJSONLoginFilter customJSONLoginFilter() {
        CustomJSONLoginFilter customJSONLoginFilter = new CustomJSONLoginFilter("/login", userDetailsService,
        		loginLogService, persistentLoginService);
        customJSONLoginFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
        customJSONLoginFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
        return customJSONLoginFilter;
    }
    
    /** （不适用前后端 分离类型）记住我功能需要使用的 repository
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
    	JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
    	jdbcTokenRepositoryImpl.setDataSource(dataSource);
    	return jdbcTokenRepositoryImpl;
    }
    */
    
    /* 密码加密（前后端分离了，不能这样用）
    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
    	builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    /** 密码 加密（由于是前后端分离，不知道能不能用），明显不能
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
    	return new BCryptPasswordEncoder();
    }
    */
	
	
	/** 设置自定义的校验类
	@Override
	protected void configure(AuthenticationManagerBuilder builder) throws Exception {
		builder.userDetailsService(userDetailsService);
	}
	
	@Bean
    public static PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    */
}
